package service

import (
	"errors"
	"fmt"
	"gitee.com/ling1314/origin-utility/constant"
	"gitee.com/ling1314/origin-utility/db"
	"gitee.com/ling1314/origin-utility/model"
	"gitee.com/ling1314/origin-utility/modules/repository"
	"gitee.com/ling1314/origin-utility/utils"
	log "github.com/sirupsen/logrus"
	"gorm.io/gorm"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"
)

type userService struct {
	baseService
}

// UserLogin 用户登录，返回token
func (ser userService) UserLogin(loginAccount model.LoginAccount) (result model.LoginSuccess, base utils.BaseClaims, err error) {
	var (
		user         *model.User
		rs           []string
		accessToken  string
		refreshToken string
	)
	user, err = repository.UserRepository.FindUserByAccount(loginAccount.Username)
	if err != nil {
		return
	}
	if user.Password != loginAccount.Password {
		return result, base, fmt.Errorf("密码错误: %v ", loginAccount)
	}
	j := utils.NewJWT()
	rs = ser.getRoles(user.UserId)
	base = utils.BaseClaims{
		UserID:    user.UserId,
		Username:  user.Username,
		Nickname:  user.Extend.Nickname,
		AccountLv: user.AccountLv,
		Role:      rs,
	}
	accessToken, err = j.CreateToken(base, constant.JWTAccessTime)
	refreshToken, err = j.CreateToken(base, constant.JWTRefreshTime)
	result = model.LoginSuccess{
		Username:     user.Extend.Nickname,
		Roles:        rs,
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		Expires:      (time.Now().Unix() + constant.JWTAccessTime) * 1000,
	}
	return
}

// getRoles 获取角色列表
func (ser userService) getRoles(uid int64) (rs []string) {
	var (
		li  []model.Role
		err error
	)
	if li, err = repository.UserRepository.FindUserRoleList(uid); err != nil {
		return []string{}
	}
	rs = make([]string, len(li))
	for i := range li {
		rs[i] = li[i].RoleCode
	}
	return rs
}

func (ser userService) GetRouterTreeForFile() (bs []byte, err error) {
	var (
		p string
	)
	if p, err = os.Executable(); err != nil {
		log.Error("os.Executable()", p, err)
		return
	}
	return ioutil.ReadFile(filepath.Join(filepath.Dir(p), "web_router.json"))
}

// VerifyCode 检查验证码是否合法
func (ser userService) VerifyCode(ver model.VeryCode) bool {
	return true
}

func (ser userService) Add(form *model.UserForm, self *utils.CustomClaims) error {
	return db.GetDB().Transaction(func(tx *gorm.DB) error {
		var (
			err error
		)
		//todo 对密码加密
		user := &model.User{
			UserId:    utils.GetSoftId(),
			Username:  form.Username,
			Password:  form.Password,
			Status:    form.Status,
			IsOnline:  model.UserOffline,
			AccountLv: form.AccountLv,
			Extend:    form.Extend(),
			Creator:   self.Nickname,
			CreatorId: self.UserID,
		}
		if _, err = repository.UserRepository.Insert(user); err != nil {
			return err
		}
		if err = repository.UserRepository.BatchInsertMapUserRole(user.UserId, form.RoleIds); err != nil {
			return err
		}
		return nil
	})
}

func (ser userService) Update(form *model.UserForm) (err error) {
	return db.GetDB().Transaction(func(tx *gorm.DB) error {
		var (
			du       *model.User
			dRoleIds []int64
			df       []int64
		)
		if du, err = repository.UserRepository.FindById(form.UserId); err != nil {
			return err
		}
		du.Status = form.Status
		du.AccountLv = form.AccountLv
		if err = repository.UserRepository.UpdateById(du); err != nil {
			log.Error(err)
			return errors.New("更新用户信息时发生数据库错误")
		}
		if dRoleIds, err = repository.UserRepository.FindRoleIds(form.UserId); err != nil {
			log.Error(err)
			return errors.New("更新用户信息时发生数据库错误")
		}
		// 检查角色列表是否有变化
		df = utils.DifferenceForInt64(dRoleIds, form.RoleIds)
		if len(df) > 0 {
			if err = repository.UserRepository.DeleteMapUserForRole(form.UserId); err != nil {
				log.Error(err)
				return errors.New("更新用户信息时发生数据库错误")
			}
			if err = repository.UserRepository.BatchInsertMapUserRole(form.UserId, form.RoleIds); err != nil {
				return err
			}
		}
		return nil
	})
}

func (ser userService) Page(user model.SearchUser, self *utils.CustomClaims) (result model.PageResult, err error) {
	var (
		uLi   []*model.User
		count int64
		li    []model.UserPage
		u     *model.User
	)
	if u, err = repository.UserRepository.FindById(self.UserID); err != nil {
		return model.PageResult{}, err
	}
	if self.AccountLv == model.AccountLvForRoot {
		uLi, count, err = repository.UserRepository.FindList(user.Username)
	} else {
		uLi, count, err = repository.UserRepository.FindListByDepId(u.DeptId)
	}
	if err != nil {
		return
	}
	li = make([]model.UserPage, len(uLi))
	for i := range uLi {
		var dep model.Dept
		if dep, err = repository.DeptRepository.SelectDeptByUserId(uLi[i].UserId); err != nil {
			return model.PageResult{}, err
		}
		var rs []model.Role
		if rs, err = repository.RoleRepository.SelectRolesByUserId(uLi[i].UserId); err != nil {
			return model.PageResult{}, err
		}
		li[i] = model.UserPage{
			User:  *uLi[i],
			Dept:  dep,
			Roles: rs,
		}
	}
	result.List = li
	result.Total = count
	return
}

func (ser userService) GetDetailById(userId int64) (result model.UserDetails, err error) {
	var (
		user *model.User
		li   []model.Role
		t    model.Dept
	)
	if user, err = repository.UserRepository.FindById(userId); err != nil {
		return
	}
	if li, err = repository.UserRepository.FindUserRoleList(userId); err != nil {
		return
	}
	if t, err = repository.UserRepository.FindUserDept(userId); err != nil {
		return
	}
	result.Roles = li
	result.UserInfo = user
	result.Dept = t
	return
}

func (ser userService) DeleteById(userId int64) error {
	return db.GetDB().Transaction(func(tx *gorm.DB) error {
		if err := repository.UserRepository.DeleteById(userId); err != nil {
			return err
		}
		if err := repository.UserRepository.DeleteMapUserForRole(userId); err != nil {
			return err
		}
		return nil
	})
}

func (ser userService) ResultPwd(userId int64) error {
	pwd := constant.DefaultPwd
	return repository.UserRepository.UpdatePwd(userId, pwd)
}

func (ser userService) GetPublicRoles() ([]*model.Role, error) {
	return repository.RoleRepository.FindAll(model.RoleIsOpenY)
}

func (ser userService) GetUserListByDeptId(deptId int64, accountLv int) (li []*model.User, err error) {
	var (
		accs []int
	)
	if accountLv == model.AccountLvForRoot {
		accs = []int{model.AccountLvForAdmin, model.AccountLvForUser}
	} else {
		accs = []int{model.AccountLvForUser}
	}
	if li, err = repository.UserRepository.FindListByDeptId(deptId, accs); err != nil {
		return
	}
	return
}
